!****************************************************
!  task partition only on two dimensions <Z,Y-split,X-split>
!                       i.e. in loop split<k,j-split,i-split> 
!  usage: make see fdag in makefile 
!***************************************************
#define alpha 0.0876
#define beta 0.0765
#define FREQ 1500000000.0
      
      SUBROUTINE dag_2d5p_master
        use dag_parallel
        implicit none
#include "aceMesh_runtime_f.h"
        include "init.for"
#define args_type_size 32      
        integer:: starti,endi,startj,endj
        integer::dest,src

     ! fortran array, default lower bound is 1

      !external timer_read
        real:: timer_read,tt,tmp
        integer::num_thread,ret
        integer:: itt,i,j,num 
        integer:: arg_size
        type(ARG_TYPE) :: each
        external :: slave_sws_2d5p_core
        external :: sws_2d5p_core
        external :: sws_2d5p_neighbor
#ifdef _LDMM
        integer,dimension(2)::block
        integer,dimension(2,2)::tileno
        integer(4) :: tile_num,ndim
#endif
     !****************************************************************
     ! we suggest use ACEMESH_NUM_THREADS as specified by dag library
     ! we do not encourage to specify num_threads in the source program
     ! we init using argument=0
     !****************************************************************
     !      delete the following lines!
     !call get_environment_variable("NUM_THREADS",thread)
     !read (thread,'(I3)') num_thread
     !***************************************************************
     !call timer_clear(1)
        call penv_slave2_gld_init()
!        call penv_slave2_read_ldm_init()
        call acemesh_runtime_init(%val(NUM_THREADS))
#ifdef _LDMM
        ndim=2
        tile_num=2
        block(1)=BLKY
        block(2)=BLKX
        addr1=loc(block)
        call ldmbuf_add_swf(%val(addr1),ndim,tile_num)
#endif
     !call timer_start(1)
        do itt=1,ITER
!       the first iteration, dest=2
!       dest,src in[1,2]
          dest=mod(itt, 2)+1
          src =mod(itt+1,2)+1
#ifdef _LDMM
          tileno(1,1)=0
          tmp=(SIZEY-2)/BLKY-1
          !tileno(2,1)=floor(((SIZEY-2)/BLKY))
          !tileno(2,1)=floor(tmp)
          tileno(2,1)=tmp
          tileno(1,2)=0
          tmp=(SIZEX-2)/BLKX-1
          !tileno(2,2)=floor(((SIZEX-2)/BLKX))
          !tileno(2,2)=floor(tmp)
          tileno(2,2)=tmp
          addr1=loc(A(1,1,dest))
          addr2=loc(tileno)
          call acemesh_register_invalidatetab_swf(%val(addr1),%val(ndim),%val(addr2))
#endif
          call acemesh_begin_split_task('2d5p')
          do i=2,SIZEX-1,BLKX
            if(i+BLKX-1 > SIZEX-1)then
              endi=SIZEX-1
            else
              endi=i+BLKX-1
            endif
            do j=2,SIZEY-1,BLKY
              if(j+BLKY-1>SIZEY-1)then
                endj=SIZEY-1
              else
                endj=j+BLKY-1
              endif
              each%starti=i
              each%endi=endi
              each%startj=j
              each%endj=endj
              each%iter=itt
              addr1 = loc(A(j,i,dest))
              call acemesh_push_wlist1(%val(1),%val(addr1),%val(NORMAL))
              addr1 = loc(A(j,i,src))
              call acemesh_push_rlist1(%val(1),%val(addr1),%val(NORMAL))
              addr1 = loc(each)
              addr2 = 0
              arg_size = args_type_size
              !print*, i,j,itt
              if ( mod(itt,2) .eq. 0)  then
                  call acemesh_task_map_master
				  call acemesh_task_generator_with_neighbors_swf2(sws_2d5p_core,addr1,arg_size,addr2,sws_2d5p_neighbor,addr1)
             else
                  call acemesh_task_generator_with_neighbors_swf2(slave_sws_2d5p_core,addr1,arg_size,addr2,sws_2d5p_neighbor,addr1)
		      endif
              call acemesh_task_set_type(%val(STENCIL_TASK))
              !if (i .eq. 2 .OR. j .eq.2 ) then
              !call acemesh_task_set_priority_id(1)
              !endif
            enddo
          enddo
          call acemesh_end_split_task()
        !call acemesh_spawn_and_wait(%val(1))
        enddo ! time iteration
        call acemesh_spawn_and_wait(%val(1))
     !call timer_stop(1)
     
        call acemesh_runtime_shutdown()
      end SUBROUTINE


!**********************************************
!     check function 
!**********************************************
      subroutine check
        use dag_parallel
        implicit none
        include "init.for"

        integer :: i,j,itt,dest,src
        real(8) :: fab
!      integer :: iter=10
 
        print *, "before check"
        dest=1  !this is for special case of zero iteration stencil
        do  itt = 1, iter
         dest=mod(itt,2)+1
         src=mod(itt+1,2)+1
         
         do i=2, SIZEX-1
         do j=2, SIZEY-1
           test(j,i,dest) = alpha * test(j,i,src) + beta * (test(j,i+1,src) + test(j,i-1,src)+ test(j+1,i,src)+ test(j-1,i,src))
         enddo
         enddo
        enddo    
      !check...
        do i=2,SIZEX-1
        do j=2,SIZEY-1
          fab=abs(test(j,i,dest)-A(j,i,dest))
          if(fab >= 1e-9)then
            print *,'j=',j,'i=',i
            print *,test(j,i,dest),'vs',A(j,i,dest)
            print *,'different!'
            return
          endif
        enddo
        enddo
        print *,"check OK"
      end subroutine
      
      subroutine my_init
        use dag_parallel
        implicit none
        include "init.for"
        integer :: i,j,k
        do i=1,SIZEX
        do j=1,SIZEY
          A(j,i,1) = (i*2.5 + j*3.3)*i/3.3
          A(j,i,2) = A(j,i,1)
          test(j,i,1) = A(j,i,1)
          test(j,i,2) = test(j,i,1)
        enddo
        enddo
      end subroutine my_init

      program main
        use dag_parallel
        implicit none
        include "init.for"
#ifdef _PROFILING
        integer(8),dimension(64)::slave_dma_count,slave_comp_count,slave_exec_count
        common /time/slave_dma_count,slave_comp_count,slave_exec_count
        integer(8) ::total_dma_count=0,total_comp_count=0,total_exec_count=0
        integer(8) ::aver_dma_count=0,aver_comp_count=0,aver_exec_count=0
        real(8) :: aver_dma_time=0.0,aver_comp_time=0.0,aver_exec_time=0.0
        integer(8) :: slave_gld_count
        common /gld/slave_gld_count

#endif
        integer i
        allocate (A(SIZEY,SIZEX,2))
        allocate (test(SIZEY,SIZEX,2))
        call my_init
        call dag_2d5p_master
#ifdef CHECK
       call check
#endif
     !tt=timer_read(1)
        !print *,'dag time including graph build:',tt
        deallocate(A)
        deallocate(test)
#ifdef _PROFILING
        do i=1,num_threads
          total_dma_count = total_dma_count+slave_dma_count(i)
          total_comp_count = total_comp_count+slave_comp_count(i)
          total_exec_count = total_exec_count+slave_exec_count(i)
        enddo
        aver_dma_count=total_dma_count/num_threads
        aver_comp_count=total_comp_count/num_threads
        aver_exec_count=total_exec_count/num_threads
        print *,total_dma_count,total_comp_count,total_exec_count
        print *,aver_dma_count,aver_comp_count,aver_exec_count
        aver_dma_time  = aver_dma_count/FREQ
        aver_comp_time = aver_comp_count/FREQ
        aver_exec_time = aver_exec_count/FREQ
        print *,aver_dma_time,aver_comp_time,aver_exec_time
!        print *,"gld count:",slave_gld_count
!        print *,"dma read count:",slave_ldmr_count
#endif
      end program main
